package appli;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import sma.agents.ObstacleAgent;
import sma.agents.channels.ObstacleChannel;

import appli.utils.Config;

/**
 * This class was develop by Gillain Basso & Jean-François Bunel and modified to works with Janus
 * 
 * This class receives data from telemeter and send them to the multiagent system
 * using Janus channel
 * 
 * @author Gillian Basso & Jean-Franois Bunel
 * @author Etienne Sainton
 * @author Arnaud Schaeffer  
 * 
 * @version 0.2
 * @since 0.1
 */
public class Telemeter implements Runnable {

	private InetAddress host;

	private Integer port;

	private Socket socket;

	private DataInputStream entry;

	private List<Integer> values;

	private Integer size;
	
	private List<ObstacleChannel> obstacleChannel = new ArrayList<ObstacleChannel>();
	
	private int rotation = 0;
	
	/**
	 * Creates a thread
	 * 
	 * @param host
	 *            - the host name, or null for the loopback address.
	 * @param port
	 *            - the port number
	 * @param s 
	 * 			  - buffer (always 0 in our case)
	 */
	public Telemeter(String host, Integer port, Integer s) {
		boolean test = false;
		
		do {
			try {
				this.host = InetAddress.getByName(host);
				this.port = new Integer(port);
				this.size = s;
	
				this.values = new ArrayList<Integer>();
				
				this.socket = new Socket(this.host, this.port);
	
				test = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		} while(test == false);
		try {
			this.entry = new DataInputStream(this.socket.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Initialize agentsObstacleList and Janus channel
	 * @param _oa Agent obstacles list
	 * @since 0.3
	 */
	public void initSMA(List<ObstacleAgent> _oa)
	{
		//Initialize agentsObstacleList
		for (int i = 0; i < _oa.size(); ++i) {
			this.values.add(Config.TELEMETER_DISTANCE);
			
			this.obstacleChannel.add(_oa.get(i).getObstacleImpl());
		}
	}

	public void run() {

		while(true){
				try {	
					boolean change = false;
					int lastObstacle;
					for (int i = 0; i < 180; ++i) {
						
						if (Config.SIMULATE) {
							if(entry != null) {
								
								//Simulate value
								int pos = entry.readInt();
								
								if(pos != this.values.get(i) ) {
									
									change = true;
									values.set(i, pos);
	
									if(Config.FILL_OBSTACLE_HOLE){
										lastObstacle = getLastObstaclePosition(pos,i);
									}else{
										lastObstacle = 0;
									}
									this.obstacleChannel.get(i).moveObstacle(pos, lastObstacle);
									
								}else{
									if(Config.FILL_OBSTACLE_HOLE){
										//if obstacle position changes
										//we look if there is a hole between obstacle agent to fill
										if(change){
											if( i > 0
												&& Math.abs(pos - values.get(i-1)) > Config.TOP_GAP){
												
												this.obstacleChannel.get(i).moveObstacle(pos, values.get(i-1));
											}else{
												this.obstacleChannel.get(i).moveObstacle(pos, -2);
											}
												
										}
										change = false;	
									}
								}		
							}						
							
						} else {
							// Sera utile pour connecter au télémetre.
							Byte v1 = entry.readByte();
							Byte v2 = entry.readByte();
							Integer val1 = v1.intValue();
							Integer val2 = v2.intValue();
							
							Integer val;
							if (val1 != null && val2 != null) {
								if (val1 < 0)
									val1 += 256;
								val = val1 + val2 * 256;
							} else
								val = 0;
							if(val != this.values.get(i)) {
								values.set(i, val);
							}
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
					System.out.println("Erreur telemtre : "+ e.getMessage() );
				}
		}
	}

	/**
	 * Returns previous obstacle position for a specified index
	 * if previous obstacle is close enough to the specified obstacle
	 * this function returns -2
	 * @param pos distance of the specified obstacle
	 * @param index of the specified obstacle
	 * @return int renvoie la position de l'obstacle
	 */
	private int getLastObstaclePosition(int pos, int index) {
		int posObstacle = 0;
		if(	index == 0){
			posObstacle = 0;
		}else{
			if(Math.abs(pos - values.get(index-1)) > Config.TOP_GAP){
				posObstacle = values.get(index-1);
			}else{
				//on déplace, mais on crée pas de bouchetrou
				posObstacle = -2;
			}
		}
		return posObstacle;
	}

	/**
	 * @return Obstacle list detected by the telemeter
	 */
	public ArrayList<Integer> getData() {
		//System.out.println("obstacles number sol : "+this.values.size());
		return new ArrayList<Integer>(this.values);
	}

	
	/**
	 * @return the port
	 */
	public Integer getPort() {
		return port;
	}

	/**
	 * @return the rotation
	 */
	public int getRotation() {
		return rotation;
	}

	/**
	 * @param rotation the rotation to set
	 */
	public void setRotation(int rotation) {
		this.rotation = rotation;
	}

}